// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_bone.h"
#include "mk_skeleton.h"
#include "mk_skeleton_data.h"

namespace MK3DE
{
	CBone::CBone(CSkeleton* pSkeleton, int index)
		: CSceneNode(NULL)
	{
		_pSkeleton = pSkeleton;
		_index = index;
	}

	CBone::~CBone()
	{

	}

	int CBone::GetParentIdx()
	{
		WORD parentBoneIdx = 0xffff;
		_pSkeleton->GetSkeletonData()->GetParentIdx(_index, parentBoneIdx);
		return parentBoneIdx;
	}

	void CBone::Update(DWORD dt, CSceneNode* parent, BOOL bChanged, BOOL relative, DWORD start, DWORD end, float factor)
	{
		__UpdateTranslation(start, end, factor);
		__UpdateRotation(start, end, factor);
		__UpdateScale(start, end, factor);
		//__UpdateScaleRotation(start, end, factor);

		BOOL changed = FALSE;
		int parentIdx = GetParentIdx();
		if (parentIdx == -1 || !relative)
			changed = UpdateAbsoluteTransformation(NULL);
		else
			changed = UpdateAbsoluteTransformation(&_pSkeleton->GetBone(parentIdx)->GetLocalTransformation());

		if (bChanged)
			changed = TRUE;

		_matLocalTransform = _absoluteTransform;
		if (parent)
			_absoluteTransform = _absoluteTransform * parent->GetAbsoluteTransformation();

		_UpdateAllEntities(dt, changed);
		_UpdateAllChildren(dt, changed);
	}

	void CBone::__UpdateTranslation(DWORD start, DWORD end, float factor)
	{
		CSkeletonData* pSkeletonData = _pSkeleton->GetSkeletonData();
		BOOL animation = FALSE;
		D3DXVECTOR3 translationStart;
		pSkeletonData->GetTranslationKey(_index, start, translationStart, animation);
		if (!animation)
			_relativeTranslation = translationStart;
		else
		{
			D3DXVECTOR3 translationEnd;
			pSkeletonData->GetTranslationKey(_index, end, translationEnd, animation);
			_relativeTranslation = translationStart + factor * (translationEnd - translationStart);
		}

		_dirty = TRUE;
	}

	void CBone::__UpdateRotation(DWORD start, DWORD end, float factor)
	{
		CSkeletonData* pSkeletonData = _pSkeleton->GetSkeletonData();
		BOOL animation = FALSE;
		D3DXQUATERNION rotationStart;
		pSkeletonData->GetRotationKey(_index, start, rotationStart, animation);
		if (!animation)
			_relativeRotation = rotationStart;
		else
		{
			D3DXQUATERNION rotationEnd;
			pSkeletonData->GetRotationKey(_index, end, rotationEnd, animation);
			D3DXQuaternionSlerp(&_relativeRotation, &rotationStart, &rotationEnd, factor);
		}

		_dirty = TRUE;
	}

	void CBone::__UpdateScale(DWORD start, DWORD end, float factor)
	{
		CSkeletonData* pSkeletonData = _pSkeleton->GetSkeletonData();
		BOOL animation = FALSE;
		D3DXVECTOR3 scaleStart;
		pSkeletonData->GetScaleKey(_index, start, scaleStart, animation);
		if (!animation)
			_relativeScale = scaleStart;
		else
		{
			D3DXVECTOR3 scaleEnd;
			pSkeletonData->GetScaleKey(_index, end, scaleEnd, animation);
			_relativeScale = scaleStart + factor * (scaleEnd - scaleStart);
		}

		_dirty = TRUE;
	}

	void CBone::__UpdateScaleRotation(DWORD start, DWORD end, float factor)
	{
		CSkeletonData* pSkeletonData = _pSkeleton->GetSkeletonData();
		BOOL animation = FALSE;
		D3DXQUATERNION scaleRotationStart;
		pSkeletonData->GetScaleRotationKey(_index, start, scaleRotationStart, animation);
		if (!animation)
			_relativeScaleRotation = scaleRotationStart;
		else
		{
			D3DXQUATERNION scaleRotationEnd;
			pSkeletonData->GetScaleRotationKey(_index, end, scaleRotationEnd, animation);
			D3DXQuaternionSlerp(&_relativeScaleRotation, &scaleRotationStart, &scaleRotationEnd, factor);
		}

		_dirty = TRUE;
	}
}